package com.orgname.glvone.glvtwo.common.exception;

import com.orgname.glvone.glvtwo.common.constant.ErrorNo;
import com.orgname.glvone.glvtwo.common.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.ResourceAccessException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.net.SocketTimeoutException;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 自定义业务异常捕捉处理
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(BusinessException.class)
    public Response businessExceptionHandler(BusinessException ex) {
        log.error("BusinessException: {} | {}", ex.getCode(), ex.getMessage());
        return Response.failedResponse(ex);
    }

    /**
     * 拦截捕捉自定义异常 ParamValidException.class
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = ParamValidException.class)
    public Response paramVerifyExceptionHandler(ParamValidException ex) {
        log.error("ParamValidException: {} | {}", ex.getCode(), ex.getMessage());
        return Response.failedResponse(ex);
    }

    /**
     * 参数校验异常捕捉处理
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler({ConstraintViolationException.class})
    public Response constraintViolationExceptionHandler(ConstraintViolationException ex) {
        log.error("ConstraintViolationException: {}", ex.getMessage());
        /*
         * 错误数为1 解析message取得code返回
         *
         * 错误数>1 将错误信息拼接后返回
         */
        return ex.getConstraintViolations().size() <= 1
                ? getResponse(ex.getMessage())
                : Response.failedResponse(
                ErrorNo.REQUEST_PARAM_VALIDATOR_FAIL,
                ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";")));
    }

    /**
     * 参数校验异常捕捉处理
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler({BindException.class})
    public Response bindExceptionHandler(BindException ex) {
        log.error("BindException: {}", ex.getMessage());
        /*
         * 错误数为1 解析message取得code返回
         *
         * 错误数>1 将错误信息拼接后返回
         */
        return ex.getErrorCount() <= 1
                ? getResponse(Objects.requireNonNull(Objects.requireNonNull(ex.getBindingResult().getFieldError()).getDefaultMessage()))
                : Response.failedResponse(
                ErrorNo.REQUEST_PARAM_VALIDATOR_FAIL,
                ex.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(";")));
    }

    /**
     * 全局异常捕捉处理
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Response exceptionHandler(Exception ex) {
        log.error("unknow exception：{} | {}", ex.getClass(), ex.getMessage());
        return Response.failedResponse("系统内部异常");
    }

    /**
     * 拦截捕捉HttpClient异常 HttpClientErrorException.class
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = {HttpClientErrorException.class, ResourceAccessException.class})
    public Response httpClientExceptionHandler(Exception ex) {
        log.error("外部系统调用失败：{}", ex.getMessage());
        return Response.failedResponse("请检查网络或服务");
    }

    /**
     * 拦截捕捉自定义异常 MethodArgumentNotValidException.class
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Response methodArgumentNotValidErrorHandler(MethodArgumentNotValidException ex) {
        log.warn("参数校验失败：{}", Objects.requireNonNull(ex.getBindingResult().getFieldError()).getDefaultMessage(), ex);
        return Response.failedResponse(ex.getBindingResult().getFieldError().getDefaultMessage());
    }

    /**
     * 运行时异常 RuntimeException.class
     *
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = RuntimeException.class)
    public Response runtimeErrorHandler(RuntimeException ex) {
        log.error("异常信息：{}", ex.getMessage());
        return Response.failedResponse("系统内部异常");
    }

    @ResponseBody
    @ExceptionHandler(value = SocketTimeoutException.class)
    public Response socketTimeoutException(SocketTimeoutException ex) {
        log.error("异常信息：{}", ex.getMessage());
        return Response.failedResponse("系统内部异常");
    }

    /**
     * 从message中解析出 code 和 message
     *
     * @param message code#message
     * @return
     */
    private Response getResponse(String message) {
        String[] args = message.split("#");
        return args.length <= 1
                ? Response.failedResponse(ErrorNo.REQUEST_PARAM_VALIDATOR_FAIL, message)
                : Response.failedResponse(Integer.valueOf(args[0]), args[1]);
    }
}